package p2pgame;

import java.util.*;

import p2pgame.message.AbstractMessage;
import p2pgame.message.AckStartGame;
import p2pgame.message.Move;
import p2pgame.message.NextTurn;
import p2pgame.message.Seed;
import p2pgame.message.SeedHash;

import rice.p2p.commonapi.Id;

//represents an OP for a Ludo game.
//A LudoApp can be an OP many times for many games at the same time.
public class Op {

	int seed = 0;
	LudoApp app;

	// Note that this is most likely not the peer's Id
	// The peer's Id is the one which happens to be closest to opId

	Id opId;

	public Game game;

	Random seedRand; // Used for generating Dice Seed

	Byte[] b= new Byte[2];
	public Map<Pair<Id,Integer>,byte[]> seedHashes;
	public Map<Pair<Id,Integer>,Integer> seeds;

	public Op(LudoApp app,Game game, Id opId){
		this.app=app;
		this.game=game;
		this.opId=opId;
		seedRand = new Random();
		seedHashes=new HashMap<Pair<Id,Integer>,byte[]>();
		seeds=new HashMap<Pair<Id,Integer>,Integer>();
	}

	// Begin exchanging seeds with other ops to calculate dice
	public void initDiceCalculation(){
		seed=seedRand.nextInt();
		byte[] seedHash = Util.hash(seed);

		AbstractMessage m = new SeedHash(app.getPeerId(),opId,game.gameId,game.turn,seedHash);
		assert(game.ops.size()==5);
		app.send(game.ops, m);				
	}


	// Player made a move
	public void move(Move msg){
		if (msg.from.equals(game.getPlayerInTurn())){
			if( game.engine.move(msg.piece) ){
				// Move is acknowledged by game engine
				game.turn++;
				initDiceCalculation();
			}
		}
	}
	// Called when a seedHash Message is received.
	// Add seed hash to list
	public void seedHash(SeedHash msg){

		Pair<Id,Integer> key=new Pair<Id,Integer>(msg.opId,msg.turn);
		if(seedHashes.get(key)==null){
			seedHashes.put(key, msg.seedHash);
		}
		// Locate the five correct ops with the same turn#
		// TODO: lock hashmap while reading it
		int count=0;
		for(Pair<Id,Integer> k: seedHashes.keySet()){
			if (k.getSecond().intValue()==msg.turn ){//&& game.ops.contains(k.getFirst())){
				count++;
			}
		}
		if (count==game.opsPerGame){
			AbstractMessage seedMsg=new Seed(app.getPeerId(),opId,game.gameId,game.turn,seed);
			// Send out Seed-message
			app.send(game.ops,seedMsg);
		}			

	}

	public void seed(Seed msg){
		Pair<Id,Integer> key=new Pair<Id,Integer>(msg.opId,msg.turn);
		if(seeds.get(key)==null){
			seeds.put(key, msg.seed);
		}
		// Locate the five correct ops with the same turn#
		// TODO: lock hashmap while reading it
		int count=0;
		int seedSum=0;
		for(Pair<Id,Integer> k: seeds.keySet()){
			if (k.getSecond().intValue()==msg.turn && 
					Util.checkHash(seeds.get(k),seedHashes.get(k))
			){
				count++;
				seedSum+=seeds.get(k).intValue();
			}
		}
		if (count==game.opsPerGame){
			seeds.clear();
			// Calculate dice with summed seed and send to players 
			game.engine.setDice(new Random(seedSum).nextInt(6)+1);
			AbstractMessage ntMsg=new NextTurn(app.getPeerId(),game);

			// Special case - wrap the nextturn message into a ackstartgame message
			if(game.turn==0){
				AbstractMessage asmsg = new AckStartGame(app.getPeerId(),game.gameId,(NextTurn)ntMsg,game.players);
				// Send Ack to all players
				app.send(game.players,asmsg);
			} else {
				app.send(game.players,ntMsg);
			}
		}			
	}

}
